/*
 * Copyright (C) 1997-2001 Id Software, Inc.
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 2 of the License, or (at
 * your option) any later version.
 *
 * This program is distributed in the hope that it will be useful, but
 * WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
 *
 * See the GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
 * 02111-1307, USA.
 *
 * =======================================================================
 *
 * Lightmap handling
 *
 * =======================================================================
 */

#include "header/local.h"

extern gllightmapstate_t gl_lms;

void R_SetCacheState ( msurface_t *surf );
void R_BuildLightMap ( msurface_t *surf, byte *dest, int stride );

void
LM_InitBlock ( void )
{
	memset( gl_lms.allocated, 0, sizeof ( gl_lms.allocated ) );
}

void
LM_UploadBlock ( qboolean dynamic )
{
	int texture;
	int height = 0;

	if ( dynamic )
	{
		texture = 0;
	}
	else
	{
		texture = gl_lms.current_lightmap_texture;
	}

	R_Bind( gl_state.lightmap_textures + texture );
	qglTexParameterf( GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR );
	qglTexParameterf( GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR );

	if ( dynamic )
	{
		int i;

		for ( i = 0; i < BLOCK_WIDTH; i++ )
		{
			if ( gl_lms.allocated [ i ] > height )
			{
				height = gl_lms.allocated [ i ];
			}
		}

		qglTexSubImage2D( GL_TEXTURE_2D,
				0,
				0, 0,
				BLOCK_WIDTH, height,
				GL_LIGHTMAP_FORMAT,
				GL_UNSIGNED_BYTE,
				gl_lms.lightmap_buffer );
	}
	else
	{
		qglTexImage2D( GL_TEXTURE_2D,
				0,
				gl_lms.internal_format,
				BLOCK_WIDTH, BLOCK_HEIGHT,
				0,
				GL_LIGHTMAP_FORMAT,
				GL_UNSIGNED_BYTE,
				gl_lms.lightmap_buffer );

		if ( ++gl_lms.current_lightmap_texture == MAX_LIGHTMAPS )
		{
			ri.Sys_Error( ERR_DROP, "LM_UploadBlock() - MAX_LIGHTMAPS exceeded\n" );
		}
	}
}

/* returns a texture number and the position inside it */
qboolean
LM_AllocBlock ( int w, int h, int *x, int *y )
{
	int i, j;
	int best, best2;

	best = BLOCK_HEIGHT;

	for ( i = 0; i < BLOCK_WIDTH - w; i++ )
	{
		best2 = 0;

		for ( j = 0; j < w; j++ )
		{
			if ( gl_lms.allocated [ i + j ] >= best )
			{
				break;
			}

			if ( gl_lms.allocated [ i + j ] > best2 )
			{
				best2 = gl_lms.allocated [ i + j ];
			}
		}

		if ( j == w )
		{
			/* this is a valid spot */
			*x = i;
			*y = best = best2;
		}
	}

	if ( best + h > BLOCK_HEIGHT )
	{
		return ( false );
	}

	for ( i = 0; i < w; i++ )
	{
		gl_lms.allocated [ *x + i ] = best + h;
	}

	return ( true );
}

void
LM_BuildPolygonFromSurface ( msurface_t *fa )
{
	int i, lindex, lnumverts;
	medge_t     *pedges, *r_pedge;
	int vertpage;
	float       *vec;
	float s, t;
	glpoly_t    *poly;
	vec3_t total;

	/* reconstruct the polygon */
	pedges = currentmodel->edges;
	lnumverts = fa->numedges;
	vertpage = 0;

	VectorClear( total );

	/* draw texture */
	poly = Hunk_Alloc( sizeof ( glpoly_t ) + ( lnumverts - 4 ) * VERTEXSIZE * sizeof ( float ) );
	poly->next = fa->polys;
	poly->flags = fa->flags;
	fa->polys = poly;
	poly->numverts = lnumverts;

	for ( i = 0; i < lnumverts; i++ )
	{
		lindex = currentmodel->surfedges [ fa->firstedge + i ];

		if ( lindex > 0 )
		{
			r_pedge = &pedges [ lindex ];
			vec = currentmodel->vertexes [ r_pedge->v [ 0 ] ].position;
		}
		else
		{
			r_pedge = &pedges [ -lindex ];
			vec = currentmodel->vertexes [ r_pedge->v [ 1 ] ].position;
		}

		s = DotProduct( vec, fa->texinfo->vecs [ 0 ] ) + fa->texinfo->vecs [ 0 ] [ 3 ];
		s /= fa->texinfo->image->width;

		t = DotProduct( vec, fa->texinfo->vecs [ 1 ] ) + fa->texinfo->vecs [ 1 ] [ 3 ];
		t /= fa->texinfo->image->height;

		VectorAdd( total, vec, total );
		VectorCopy( vec, poly->verts [ i ] );
		poly->verts [ i ] [ 3 ] = s;
		poly->verts [ i ] [ 4 ] = t;

		/* lightmap texture coordinates */
		s = DotProduct( vec, fa->texinfo->vecs [ 0 ] ) + fa->texinfo->vecs [ 0 ] [ 3 ];
		s -= fa->texturemins [ 0 ];
		s += fa->light_s * 16;
		s += 8;
		s /= BLOCK_WIDTH * 16; /* fa->texinfo->texture->width; */

		t = DotProduct( vec, fa->texinfo->vecs [ 1 ] ) + fa->texinfo->vecs [ 1 ] [ 3 ];
		t -= fa->texturemins [ 1 ];
		t += fa->light_t * 16;
		t += 8;
		t /= BLOCK_HEIGHT * 16; /* fa->texinfo->texture->height; */

		poly->verts [ i ] [ 5 ] = s;
		poly->verts [ i ] [ 6 ] = t;
	}

	poly->numverts = lnumverts;
}

void
LM_CreateSurfaceLightmap ( msurface_t *surf )
{
	int smax, tmax;
	byte    *base;

	if ( surf->flags & ( SURF_DRAWSKY | SURF_DRAWTURB ) )
	{
		return;
	}

	smax = ( surf->extents [ 0 ] >> 4 ) + 1;
	tmax = ( surf->extents [ 1 ] >> 4 ) + 1;

	if ( !LM_AllocBlock( smax, tmax, &surf->light_s, &surf->light_t ) )
	{
		LM_UploadBlock( false );
		LM_InitBlock();

		if ( !LM_AllocBlock( smax, tmax, &surf->light_s, &surf->light_t ) )
		{
			ri.Sys_Error( ERR_FATAL, "Consecutive calls to LM_AllocBlock(%d,%d) failed\n", smax, tmax );
		}
	}

	surf->lightmaptexturenum = gl_lms.current_lightmap_texture;

	base = gl_lms.lightmap_buffer;
	base += ( surf->light_t * BLOCK_WIDTH + surf->light_s ) * LIGHTMAP_BYTES;

	R_SetCacheState( surf );
	R_BuildLightMap( surf, base, BLOCK_WIDTH * LIGHTMAP_BYTES );
}

void
LM_BeginBuildingLightmaps ( model_t *m )
{
	static lightstyle_t lightstyles [ MAX_LIGHTSTYLES ];
	int i;
	unsigned dummy [ 128 * 128 ];

	memset( gl_lms.allocated, 0, sizeof ( gl_lms.allocated ) );

	r_framecount = 1; /* no dlightcache */

	R_EnableMultitexture( true );
	R_SelectTexture( QGL_TEXTURE1 );

	/* setup the base lightstyles so the lightmaps won't have to be regenerated
	 * the first time they're seen */
	for ( i = 0; i < MAX_LIGHTSTYLES; i++ )
	{
		lightstyles [ i ].rgb [ 0 ] = 1;
		lightstyles [ i ].rgb [ 1 ] = 1;
		lightstyles [ i ].rgb [ 2 ] = 1;
		lightstyles [ i ].white = 3;
	}

	r_newrefdef.lightstyles = lightstyles;

	if ( !gl_state.lightmap_textures )
	{
		gl_state.lightmap_textures  = TEXNUM_LIGHTMAPS;
	}

	gl_lms.current_lightmap_texture = 1;
	gl_lms.internal_format = gl_tex_solid_format;

	/* initialize the dynamic lightmap texture */
	R_Bind( gl_state.lightmap_textures + 0 );
	qglTexParameterf( GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR );
	qglTexParameterf( GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR );
	qglTexImage2D( GL_TEXTURE_2D,
			0,
			gl_lms.internal_format,
			BLOCK_WIDTH, BLOCK_HEIGHT,
			0,
			GL_LIGHTMAP_FORMAT,
			GL_UNSIGNED_BYTE,
			dummy );
}

void
LM_EndBuildingLightmaps ( void )
{
	LM_UploadBlock( false );
	R_EnableMultitexture( false );
}

